(ns gen-changelog
  (:require [clojure.java.shell :refer [sh]]
            [clojure.string :as str])
  (:import (java.io File))
  (:gen-class))

; fetch all tags
(sh "git" "fetch" "git@github.com:clojure/core.typed.git" "master" "--tags")

; [-> '[MajorLong MinorLong PatchLong]]
(defn git-version []
  (let [gv (-> (sh "git" "--version")
               :out
               str/trim-newline)
        prefix "git version "
        _ (assert (.startsWith gv prefix))
        version (subs gv (count prefix))
        [major minor patch & more :as match] (mapv #(Long/parseLong %) (str/split version #"\."))
        _ (assert (not more))]
    match))


(let [[major minor _] (git-version)]
  (assert (and (<= 2 major)
               (<= 25 minor))
          "Must have git version 2.25.0 or higher"))

; Str
(def changelog
  (-> (slurp "changelog.edn")
      read-string))

; (Map Kw Str)
(def modules
  (-> (slurp "../modules.edn")
      read-string))

; (Vec VersionStr)
; - VersionStr == "major.minor.patch"
(def tags
  (->> (sh "git" "tag" "-l")
       :out
       java.io.StringReader.
       java.io.BufferedReader.
       line-seq
       (mapcat (fn [^String s]
                 (when-let [version (some (fn [prefix]
                                            (when (.startsWith s prefix)
                                              (subs s (count prefix))))
                                          ["" "core.typed-pom-"])]
                   [version s])))
       (apply hash-map)))

(assert (seq tags)
        "No tags found, run `git fetch origin --tags`")

(defn parse-version-changes [module [version & args :as entry]]
  (assert (vector? entry) entry)
  (let [tag-name (tags version)
        parse-body (fn self
                     ([note] (self note -1))
                     ([note depth]
                      {:pre [((some-fn string? vector?) note)]}
                      (if (string? note)
                        (str (apply str (repeat (* 2 (if (#{-1} depth) 0 depth)) " ")) "- " note "\n")
                        (apply str (mapv #(self % (inc depth)) note)))))
        out-str (str "# " version (when-not tag-name "-SNAPSHOT") "\n\n"
                     (when tag-name
                       (let [release-date (-> (sh "git" "log" "-1" "--format=%as" tag-name)
                                              :out
                                              str/trim-newline)]
                         (str "Released: " release-date "\n\n")))
                     (cond
                       (empty? args) "No changes\n"

                       ;master changelog
                       (#{:changelog/all} module)
                       (apply str
                              (interpose
                                "\n"
                                (map (fn [[lib-kw & args :as all]]
                                       (assert (vector? all))
                                       (apply str (str "## " (name lib-kw) "\n\n")
                                              (map parse-body args)))
                                     args)))

                       ;module changelog
                       :else 
                       (str
                         (apply str
                                (mapcat (fn [[lib-kw & args :as all]]
                                          (assert (vector? all))
                                          (when (= (name lib-kw) (name module))
                                            [(apply str (map parse-body args))]))
                                        args))
                         "\n")))]
    out-str))

(defn forge-master-changelog [changelog]
  (apply str "<!--- Do not edit, generated by `doc/changelog/changelog.edn` -->\n"
         (map #(parse-version-changes :changelog/all %) changelog)))

(defn forge-module-changelog [module changelog]
  (apply str "<!--- Do not edit, generated by `doc/changelog/changelog.edn` -->\n"
         (map #(parse-version-changes module %) changelog)))

(defn spit-master-changelog [changelog]
  (spit "../../CHANGELOG.md"
        (forge-master-changelog changelog)))

(defn spit-module-changelog [module module-dir changelog]
  (spit (str "../../" module-dir "/CHANGELOG.md")
        (forge-module-changelog module changelog)))

(defn -main [& args]
  (spit-master-changelog changelog)
  (run! #(spit-module-changelog (key %) (val %) changelog) modules)
  (shutdown-agents)
  )

(comment
  (spit-master-changelog changelog)
  (mapv spit-module-changelog modules changelog)
  (print (forge-master-changelog changelog))
  (print (forge-module-changelog :analyzer.common changelog))
  )
